#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <sysexits.h>
#include <sys/wait.h>
#include <unistd.h>

#include "common.h"
#include "config.h"

char *quota_cmd(char *flag, char *mark, long quota) {
  const char * const fmt = PATH_IPTABLES " -t mangle -%s charon-quota -m mark --mark %s -m quota --quota %ld -j MARK --set-mark 0x3001";
  int len = strlen(fmt) + 100;
  char buf[len];

  snprintf(buf, len, fmt, flag, mark, quota);
  return g_strdup(buf);
}

void cmd_exec(gpointer cmd, gpointer notused) {
  //fprintf(stderr, "Executing: %s\n", (char *)cmd);
  system(cmd);
  g_free(cmd);
}

void freeRemuneration(struct remuneration *r) {
  xmlFree(r->currency);
  free(r);
}

int main(int argc, char **argv) {
  FILE *f;
  char buf[150];
  GHashTable *tab;
  DIR *dir;
  struct dirent *d;
  int exitcode;

  libSetup();
  while(1) {
    tab = g_hash_table_new(g_str_hash, g_str_equal);
    GSList *exec = NULL;

    f = popen(PATH_IPTABLES " -t mangle -L charon-quota -n", "r");
    assert(f);
    while(fgets(buf, 150, f)) {
      int i = 0;
      char *src = "", *str = NULL;
      unsigned long quota = 0;

      str = strtok(buf, " \t\r\n");
      do {
	//      fprintf(stderr, "Looking at %d:'%s'\n", i + 1, str);
	switch(i++) {
	case 0:
	  if(!strcmp(str, "Chain")) /* iptables header */
	    goto nextline;
	  if(!strcmp(str, "target")) /* iptables header */
	    goto nextline;
	  break;			/* "ACCEPT" */
	case 1:			/* "all" */
	  break;
	case 2:			/* "--" */
	  break;
	case 3:			/* ip src */
	  break;
	case 4:			/* ip dst */
	  //	src = str[i];
	  break;
	case 5:			/* "MARK" */
	  break;
	case 6:			/* "match" */
	  break;
	case 7:			/* mark */
	  src = str;
	  break;
	case 8:			/* "quota:" */
	  break;
	case 9:			/* quota left */
	  quota = atol(str);
	  break;
	case 10:			/* "bytesMARK" */
	  break;
	case 11:			/* "set" */
	  break;
	case 12:			/* "0x3001" */
	  break;
	default:
	  fprintf(stderr, "Failed to parse line %s\n", buf);
	  goto nextline;
	}
	//      printf("Split %d (%d): %s\n", i, strlen(str[i]), str[i]);
      } while((str = strtok(NULL, " \t\r\n")));
      //fprintf(stderr, "Src %s quota %ld\n", src, quota);
      if(quota) {
	char *key;
	long value;
	
	if(g_hash_table_lookup_extended(tab, src, (gpointer)&key, (gpointer)&value)) {
	  if(quota > value)
	    g_hash_table_insert(tab, key, (gpointer)quota);
	} else
	  g_hash_table_insert(tab, g_strdup(src), (gpointer)quota);
#if 0	
	if(quota > (unsigned long)g_hash_table_lookup(tab, src))
	  g_hash_table_insert(tab, g_strdup(src), (gpointer)quota);
	else
	  fprintf(stderr, "skipping\n");
#endif	
      } else {
	//fprintf(stderr, "queueing quota_cmd('D', %s, %ld)\n", src, quota);
	exec = g_slist_prepend(exec, quota_cmd("D", src, quota));
      }
    nextline:
      ;
    }
    exitcode = pclose(f);
    if(!WIFEXITED(exitcode) || WEXITSTATUS(exitcode)) {
      fprintf(stderr, "Bad result from table scan: %x (%d)\n", exitcode, WEXITSTATUS(exitcode));
      exit(EX_OSERR);
    }
    g_slist_foreach(exec, cmd_exec, NULL);
    g_slist_free(exec);
    exec = NULL;
    dir = opendir(LOCALSTATEDIR);
    while((d = readdir(dir))) {
      xmlDocPtr doc;
      xmlNodePtr root, node;
      char *ip;
      unsigned long quantum;
    
      int l = strlen(d->d_name);
      if(l < 4)
	continue;
      if(strcmp(d->d_name + l - 4, ".xml"))
	continue;
      // fprintf(stderr, "Found file %s\n", d->d_name);
      ip = d->d_name;
      while(*ip == '0')
	ip++;
      chdir(LOCALSTATEDIR);
      doc = xmlParseFile(d->d_name);
      if(!doc)
      	continue;
      *--ip = 'x';
      --ip;
      *index(ip, '.') = 0;
      root = xmlDocGetRootElement(doc);
      node = findOrDie(root, ns_config, "ip");
      //    ip = xmlNodeListGetString(node->doc, node->xmlChildrenNode, 1);
      node = findOrDie(root, ns_mikolaj, "remuneration");
      node = findOrDie(node, ns_mikolaj, "ratingQuantum");
      quantum = atol(xmlNodeListGetString(node->doc, node->xmlChildrenNode, 1));
      //fprintf(stderr, "Found quantum %ld for ip %s\n", quantum, ip);
      if((quantum / 10) >= (unsigned long)g_hash_table_lookup(tab, ip)) {
	struct remuneration *rem;

	chdir("/tmp");
	//fprintf(stderr, "Off to grab more quota coz %ld > %ld\n",
	//	quantum / 10, (unsigned long)g_hash_table_lookup(tab, ip));
	rem = makeRemuneration(findOrDie(root, ns_mikolaj, "remuneration"));
	if(doDebitAuthority(findOrDie(root, "urn:dosh", "authority"), rem)) {
	  //fprintf(stderr, "debit failed\n");
	} else {
	  //fprintf(stderr, "debit ok!\n");
	  if(doConfigureNetwork(strtoul(ip, NULL, 16), quantum)) {
	    //fprintf(stderr, "adding quota failed!\n");
	  } else {
	    // fprintf(stderr, "adding quota succeeded!\n");
	  }
	}
	//freeRemuneration(rem);
      }
      xmlFreeDoc(doc);
    }
    closedir(dir);
    sleep(5);
    assert(!execv(argv[0], argv));
  }
  return 0;
}
